﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using LockCrypt.Converters;
using LockCrypt.Core;
using LockCrypt.DAL;
using Microsoft.Win32;
using Path=System.IO.Path;

namespace LockCrypt {
    public class LockCryptWPFBLL {
        /// <summary>
        /// Converts a SecureString to a string.
        /// </summary>
        /// <param name="pass">The SecureString.</param>
        /// <returns>The contents of the SecureString as a string</returns>
        public static string SecureStringToString(SecureString pass) {
            if(pass == null)
                throw new ArgumentNullException("pass");

            IntPtr unmanagedString = IntPtr.Zero;
            try {
                unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(pass);
                return Marshal.PtrToStringUni(unmanagedString);
            } finally {
                Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);
            }
        }

        /// <summary>
        /// Converts a string to a SecureString.
        /// </summary>
        /// <param name="password">The string.</param>
        /// <returns>A SecureString representing the string</returns>
        public static SecureString StringToSecureString(string password) {
            if(password == null)
                throw new ArgumentNullException("password");
            unsafe {
                fixed (char* passwordChars = password) {
                    var securePassword = new SecureString(passwordChars, password.Length);
                    return securePassword;
                }
            }
        }

        /// <summary>
        /// Generates a descriptor.
        /// </summary>
        /// <param name="provider">The database provider.</param>
        /// <param name="initialSavePath">The initial path for the save dialog.</param>
        public static void SaveDescriptor(IDbProvider provider, string path) {
            string[] descriptorLines = LockCryptWPFBLL.GenerateDescriptor(provider);
            File.WriteAllLines(path, descriptorLines, Encoding.UTF8);
        }

        /// <summary>
        /// Generates a database descriptor for a provider.
        /// </summary>
        /// <param name="provider">The provider.</param>
        /// <returns>A string describing the database which can be used as a descriptor</returns>
        public static string[] GenerateDescriptor(IDbProvider provider) {
            string providerName = provider.DatabaseType.ToString(),
                   encryptionName = provider.Encryption.EncryptionAlgorithm.ToString(),
                   pass = provider.Encryption.EncryptionKey;
            string[] dbLines = null;
            if(provider is FileBasedDatabase) {
                dbLines = new[] {
                    string.Format("-db={0}", ((FileBasedDatabase)provider).FileName),
                };
            } else if(provider is NetworkDatabase) {
                dbLines = new[] {
                    string.Format("-dbhost={0}", ((NetworkDatabase)provider).Credentials.Hostname),
                    string.Format("-dbport={0}", ((NetworkDatabase)provider).Credentials.Port),
                    string.Format("-dbuser={0}", ((NetworkDatabase)provider).Credentials.Username),
                    string.Format("-dbpass={0}", ((NetworkDatabase)provider).Credentials.Password),
                    string.Format("-dbdatabase={0}", ((NetworkDatabase)provider).Credentials.DatabaseName),
                    string.Format("-dbprefix={0}", ((NetworkDatabase)provider).Credentials.TablePrefix),
                };
            } else {
                dbLines = new string[0];
            }
            string[] lines = new[] {
                "lockcrypt db",
                dbLines.Aggregate((args, arg) => args+'\n'+arg),
                string.Format("-mode={0}", providerName),
                string.Format("-encryption={0}", encryptionName),
                //string.Format("-pw={0}", pass),
            };
            return lines;
        }

        /// <summary>
        /// Tries to load an icon from the /Icons directory using the naming convention 'filename_size.png'. If the file does not exist,
        /// falls back to 'filename.png'. If both files are missing, uses the default icon.
        /// </summary>
        /// <param name="filename">The filename.</param>
        /// <param name="requiredSize">The required size.</param>
        /// <returns>An image of the specified size, or the default icon.</returns>
        public static BitmapImage GetSizedImage(string filename, string requiredSize) {
            string appDir = Utils.GetApplicationPath();
            // try requested size
            string requestedImage = Path.Combine(appDir, string.Format("Icons{0}{1}_{2}.png", Path.DirectorySeparatorChar, filename, requiredSize));
            if(!File.Exists(requestedImage)) {
                // doesn't exist, try generic file
                requestedImage = Path.Combine(appDir, string.Format("Icons{0}{1}.png",Path.DirectorySeparatorChar, filename));
                if(!File.Exists(requestedImage)) {
                    // also doesn't exist, use default icon
                    requestedImage = string.Format("pack://application:,,,/Resources/Images/default_{0}.png", requiredSize);
                }
            }
            BitmapImage img = new BitmapImage();
            img.BeginInit();
            img.CacheOption = BitmapCacheOption.OnLoad;
            img.UriSource = new Uri(requestedImage);
            img.EndInit();
            return img;
        }

        /// <summary>
        /// Adds an account to a document.
        /// </summary>
        /// <param name="document">The document.</param>
        /// <param name="account">The account to add.</param>
        public static void AddAccountToDocument(FlowDocument document, Account account) {
            IValueConverter iconConverter = new IconConverter();
            Grid hdrGrid = new Grid() { HorizontalAlignment = HorizontalAlignment.Stretch };
            hdrGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(0, GridUnitType.Auto) });
            hdrGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(0, GridUnitType.Auto) });
            hdrGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(0, GridUnitType.Auto) });
            hdrGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width= new GridLength(0, GridUnitType.Auto) });
            hdrGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) });
            hdrGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(0, GridUnitType.Auto) });

            // name and icon
            Image imgIcon = new Image() {
                Source = iconConverter.Convert(account.Icon, typeof(ImageSource), 64, null) as ImageSource,
                Margin = new Thickness(1, 1, 6, 1),
                //Stretch= Stretch.None
                Width = 32,
                Height = 32,
                VerticalAlignment = VerticalAlignment.Center
            };
            Grid.SetRow(imgIcon, 0);
            Grid.SetColumn(imgIcon, 0);
            Grid.SetRowSpan(imgIcon, 3);
            hdrGrid.Children.Add(imgIcon);
            
            TextBlock txtName = new TextBlock() { FontSize = 24, Text=account.Name, VerticalAlignment= VerticalAlignment.Center };
            Grid.SetRow(txtName, 0);
            Grid.SetColumn(txtName, 1);
            Grid.SetRowSpan(txtName, 3);
            hdrGrid.Children.Add(txtName);

            // credits (header)
            TextBlock txtCredits = new TextBlock(new Run("LockCrypt by Harry Jennerway")) { FontSize = 6, TextAlignment = TextAlignment.Right };
            Grid.SetRow(txtCredits, 0);
            Grid.SetColumn(txtCredits, 2);
            hdrGrid.Children.Add(txtCredits);
            TextBlock txtcreditsLink = new TextBlock(new Run("http://www.lockcrypt.com")) { FontSize = 6, TextAlignment = TextAlignment.Right };
            Grid.SetRow(txtcreditsLink, 1);
            Grid.SetColumn(txtcreditsLink, 2);
            hdrGrid.Children.Add(txtcreditsLink);
            document.Blocks.Add(new BlockUIContainer(hdrGrid));

            BlockUIContainer separatorContainer = new BlockUIContainer(new Line() {
                X1 = 1,
                Fill = Brushes.DarkBlue,
                Stretch = Stretch.Fill
            });
            document.Blocks.Add(separatorContainer);

            // fields
            Table tblFields = new Table();
            document.Blocks.Add(tblFields);
            tblFields.Columns.Add(new TableColumn() { Width = new GridLength(2, GridUnitType.Star) });
            tblFields.Columns.Add(new TableColumn() { Width = new GridLength(3, GridUnitType.Star) });
            TableRowGroup fldGroup = new TableRowGroup();
            tblFields.RowGroups.Add(fldGroup);
            foreach(IField field in account.Fields) {
                TableRow row = new TableRow();
                TableCell clName = new TableCell(new Paragraph(new Run(field.Name)) {
                    FontWeight = FontWeights.Bold,
                    TextAlignment = TextAlignment.Right
                });
                row.Cells.Add(clName);
                TableCell clValue = new TableCell(new Paragraph(new Run(field.Value)));
                row.Cells.Add(clValue);
                fldGroup.Rows.Add(row);
            }
            document.Blocks.Add(new Paragraph(new Run(account.Notes)));
        }

        /// <summary>
        /// Adds a group to a document.
        /// </summary>
        /// <param name="document">The document.</param>
        /// <param name="group">The group to add.</param>
        public static void AddGroupToDocument(FlowDocument document, IGroup group) {
            IValueConverter iconConverter = new IconConverter();
            Grid hdrGrid = new Grid() { HorizontalAlignment = HorizontalAlignment.Stretch };
            hdrGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(0, GridUnitType.Auto) });
            hdrGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(0, GridUnitType.Auto) });
            hdrGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(0, GridUnitType.Auto) });
            hdrGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(0, GridUnitType.Auto) });
            hdrGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) });
            hdrGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(0, GridUnitType.Auto) });

            // name and icon
            Image imgIcon = new Image() {
                Source = iconConverter.Convert(group.Icon, typeof(ImageSource), 64, null) as ImageSource,
                Margin = new Thickness(1, 1, 6, 1),
                //Stretch= Stretch.None
                Width = 32,
                Height = 32,
                VerticalAlignment = VerticalAlignment.Center
            };
            Grid.SetRow(imgIcon, 0);
            Grid.SetColumn(imgIcon, 0);
            Grid.SetRowSpan(imgIcon, 3);
            hdrGrid.Children.Add(imgIcon);

            TextBlock txtName = new TextBlock() { FontSize = 24, Text = group.Name, VerticalAlignment = VerticalAlignment.Center };
            Grid.SetRow(txtName, 0);
            Grid.SetColumn(txtName, 1);
            Grid.SetRowSpan(txtName, 3);
            hdrGrid.Children.Add(txtName);

            // credits (header)
            TextBlock txtCredits = new TextBlock(new Run("LockCrypt by Harry Jennerway")) { FontSize = 6, TextAlignment = TextAlignment.Right };
            Grid.SetRow(txtCredits, 0);
            Grid.SetColumn(txtCredits, 2);
            hdrGrid.Children.Add(txtCredits);
            TextBlock txtcreditsLink = new TextBlock(new Run("http://www.lockcrypt.com")) { FontSize = 6, TextAlignment = TextAlignment.Right };
            Grid.SetRow(txtcreditsLink, 1);
            Grid.SetColumn(txtcreditsLink, 2);
            hdrGrid.Children.Add(txtcreditsLink);

            document.Blocks.Add(new BlockUIContainer(hdrGrid));

            BlockUIContainer separatorContainer = new BlockUIContainer(new Line() {
                X1 = 1,
                Fill = Brushes.DarkBlue,
                Stretch = Stretch.Fill
            });
            document.Blocks.Add(separatorContainer);

            ContentControl ctl = new ContentControl() { HorizontalAlignment = HorizontalAlignment.Stretch };
            ctl.ContentTemplate = Application.Current.Resources["GroupTilesTemplate"] as DataTemplate;
            ctl.Content = group;
            document.Blocks.Add(new BlockUIContainer(ctl));
        }

        /*public static List GenerateAccountList(IEnumerable<KeyValuePair<Group, List<IAccount>>> groupedAccounts) {
            List lst = new List();
            Brush groupHeaderBackgroundBrush = Application.Current.Resources["hoverGradient"] as Brush;
            IValueConverter iconConverter = new IconConverter();
            foreach(var groupPair in groupedAccounts) {
                Grid headerGrid = new Grid();
                headerGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(0, GridUnitType.Auto) });
                headerGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(0, GridUnitType.Star) });
                Border headerBorder = new Border() {
                    Background = groupHeaderBackgroundBrush,
                    Opacity = 0.5
                };
                Grid.SetColumn(headerBorder, 0);
                Grid.SetColumnSpan(headerBorder, 2);
                headerGrid.Children.Add(headerBorder);
                Image imgGroup = new Image() {
                    Source = iconConverter.Convert(groupPair.Key.Icon, typeof(ImageSource), 64, null) as ImageSource,
                    Margin = new Thickness(1, 1, 6, 1),
                    Width = 16,
                    Height = 16
                };
                Grid.SetColumn(imgGroup, 0);
                headerGrid.Children.Add(imgGroup);
                TextBlock txtGroupName = new TextBlock(new Run(groupPair.Key.Name)) {
                    TextWrapping = TextWrapping.Wrap,
                    TextTrimming = TextTrimming.CharacterEllipsis,
                    FontSize = 24,
                    VerticalAlignment = VerticalAlignment.Center
                };
                Grid.SetColumn(txtGroupName, 1);
                headerGrid.Children.Add(txtGroupName);
                BlockUIContainer headerContainer = new BlockUIContainer(headerGrid);
                ListItem lstItem = new ListItem();
                lstItem.Blocks.Add(headerContainer);
                lst.ListItems.Add(lstItem);
            }
            return lst;
        }*/
    }
}
